﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using GameBoard;
using Microsoft.Win32;
using ServerIntegration;

namespace CombatAutomationTheater
{
    /// <summary>
    ///     Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly PictureSelectionViewModel _pictureSelectionViewModel = PictureSelectionViewModel.Instance;
        private readonly Paragraph RichTextParagraph;
        private readonly List<GameBoardVisual_Window> _gameBoardWindows = new List<GameBoardVisual_Window>();
        private GameBoardVisual _targetBoard;

        public MainWindow()
        {
            InitializeComponent();
            DataContext = CombatService.RosterViewModel;
            CombatService.RosterViewModel.PopulateCharacters(Directory.GetCurrentDirectory() + "\\Sheets");
            CombatService.RosterViewModel.ShowErrors();
            MakeDefaultBoards();

            VisualsService.BoardsViewModel.Intialize();
            RegisterViewmodelEvents();

            CombatService.RosterViewModel.RulesetSelected += RulesetSelectedResponse;

            CombatService.RosterViewModel.ReportTextEvent += UpdateResultsRichTextBox;
            CombatService.RosterViewModel.ClearReportTextEvent += ClearResultsRichTextBox;
            RichTextParagraph = new Paragraph();
            Results_RichTextBox.Document = new FlowDocument(RichTextParagraph);
        }

        private void MakeDefaultBoards()
        {
            foreach (var cur in _gameBoardWindows)
            {
                cur.Close();
            }
            _gameBoardWindows.Clear();
            BoardsViewModel.Instance.ClearAllBoards();

            VisualControl_BoardSpace_DockPanel.Children.Clear();
            MainBoard_DockPanel.Children.Clear();
            _targetBoard = null;

            var boardForMainTab =
                VisualsService.BoardsViewModel.CreateAndRegisterNewBoard(BoardsViewModel.BoardTypes.Main);
            var boardForVisualTab =
                VisualsService.BoardsViewModel.CreateAndRegisterNewBoard(BoardsViewModel.BoardTypes.VisualTab);
            _targetBoard =
                VisualsService.BoardsViewModel.CreateAndRegisterNewBoard(BoardsViewModel.BoardTypes.Target);


            VisualControl_BoardSpace_DockPanel.Children.Add(boardForVisualTab);
            MainBoard_DockPanel.Children.Add(boardForMainTab);

            MakeNewGameBoardWindow();
        }

        private void DeregisterViewmodelEvents()
        {
            VisualsViewModel.Instance.PieceSelected -= VisualPieceSelected;
            VisualsViewModel.Instance.ClearSelectedPieces -= ClearSelectedPieces;
            VisualsViewModel.Instance.PieceMoved -= OnVisualPieceMoved;
        }

        private void RegisterViewmodelEvents()
        {
            VisualsViewModel.Instance.PieceSelected += VisualPieceSelected;
            VisualsViewModel.Instance.ClearSelectedPieces += ClearSelectedPieces;
            VisualsViewModel.Instance.PieceMoved += OnVisualPieceMoved;
        }

        private void OnVisualPieceMoved(object sender, EventArgs e)
        {
            var pieceEvent = e as PieceMovedEventsArg;
            if (pieceEvent != null)
            {
                if (pieceEvent.MoverID != null)
                {
                    CharacterSheet matchingChar = CombatService.RosterViewModel.ActiveRoster.
                        FirstOrDefault(x => x.UniqueCharacterID == pieceEvent.MoverID);
                    if (matchingChar != null)
                    {
                        matchingChar.HasMoved = true;
                    }
                }
            }
        }


        private void ClearResultsRichTextBox(object sender, EventArgs e)
        {
            RichTextParagraph.Inlines.Clear();
        }

        private void UpdateResultsRichTextBox(object sender, EventArgs e)
        {
            var textEvent = e as ReportTextEventArgs;
            RichTextParagraph.Inlines.Add(new Bold(new Run(textEvent.Message))
            {
                Foreground = textEvent.DisplayColor,
                FontSize = textEvent.FontSize,
            });
        }

        private void RulesetSelectedResponse(object sender, EventArgs e)
        {
            var ruleEvent = e as RulesetSelectedEventArgs;
            if (ruleEvent != null)
            {
                if (ruleEvent.SelectedRuleset.Equals(RulesetSelectedEventArgs.ClearRulesetString))
                {
                    CustomCombatPanel.Children.Clear();
                }
                else
                {
                    CustomCombatPanel.Children.Add(CombatService.RosterViewModel.ControlFor(ruleEvent.SelectedRuleset));
                    CombatService.RosterViewModel.LoadDamageFor(ruleEvent.SelectedRuleset);
                }
            }
        }

        private void ClearSelectedPieces(object sender, EventArgs e)
        {
            ActiveCharacters_ListBox.SelectedItems.Clear();
        }

        private void VisualPieceSelected(object sender, EventArgs e)
        {
            var pieceEvent = e as PieceSelectedEventArgs;
            if (pieceEvent != null)
            {
                CharacterSheet matchingChar =
                    CombatService.RosterViewModel.ActiveRoster.FirstOrDefault(
                        x => x.UniqueCharacterID == pieceEvent.SelectedPieceID);

                if (matchingChar != null)
                {
                    ActivateCharacter(matchingChar, false);
                }
            }
        }

        private void ActivateCharacter(CharacterSheet matchingChar, bool zoomToCharacter)
        {
            ActiveCharacters_ListBox.SelectedItems.Add(matchingChar);
            ActiveCharacters_ListBox.ScrollIntoView(matchingChar);
            if (zoomToCharacter && matchingChar.HasVisual)
                BoardsZoomTo(new List<Guid> { matchingChar.UniqueCharacterID });
        }


        private void ActiveCharacters_ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (ActiveCharacters_ListBox.SelectedItems.Count > 0)
                CombatService.RosterViewModel.SelectedActiveCharacter =
                    ActiveCharacters_ListBox.SelectedItems[ActiveCharacters_ListBox.SelectedItems.Count - 1] as
                        CharacterSheet;
            else
                CombatService.RosterViewModel.SelectedActiveCharacter = null;
            CombatService.RosterViewModel.ResetToActive(e.AddedItems, e.RemovedItems);
        }

        private void Reset_Health_Button_Click(object sender, RoutedEventArgs e)
        {
            if (!CheckValidActive())
                return;
            CombatService.RosterViewModel.ResetHealth(ActiveList());
        }

        private void Initiative_Button_Click(object sender, RoutedEventArgs e)
        {
            bool someActiveLeft = CombatService.RosterViewModel.ActiveRoster.Any(x => !x.HasAttacked && !x.HasMoved);
            if (someActiveLeft)
            {
                someActiveLeft = !CombatService.RosterViewModel.ActiveRoster.All(x => x.CurInitiative == -1);
            }
            if (someActiveLeft)
            {
                if (
                    MessageBox.Show("Some characters have not acted. Roll initiative anyway?", "Confirm New Round",
                        MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.No)
                {
                    return;
                }
            }
            CombatService.RosterViewModel.RollInitiative();
            CombatService.RosterViewModel.CurrentRound++;
            CurrentRound_Label.Content = "Round " + CombatService.RosterViewModel.CurrentRound;
            CombatService.RosterViewModel.NewRound();
        }

        private static bool IsTextNumeric(string text)
        {
            text = text.Trim();
            var regex = new Regex("[^0-9-]"); //regex that matches disallowed text
            bool isGood = !regex.IsMatch(text);
            return isGood;
        }


        private void Modifier_TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
        {
            bool isGood = IsTextNumeric(e.Text);
            e.Handled = !isGood;
        }

        // Use the DataObject.Pasting Handler 
        private void Modifier_TextBox_Pasting(object sender, DataObjectPastingEventArgs e)
        {
            if (e.DataObject.GetDataPresent(typeof(String)))
            {
                var text = (String)e.DataObject.GetData(typeof(String));
                if (!IsTextNumeric(text))
                {
                    e.CancelCommand();
                }
            }
            else
            {
                e.CancelCommand();
            }
        }

        private void SelectedTargetButton_Click(object sender, RoutedEventArgs e)
        {
            var t = new NewSelectTarget(_targetBoard, ActiveCharacters_ListBox.SelectedItems);
            t.ShowDialog();

            if (!t.WasCancel && t.SelectedTarget != null)
            {
                CombatService.RosterViewModel.SetTargets(t.Attackers.ToList(), t.OtherTraits.ToList(), t.SelectedTarget,
                    t.MainAttack, t.DamageType);
            }
        }

        private void Attack_Target_Button_Click(object sender, RoutedEventArgs e)
        {
            if (!CheckValidActive())
                return;
            CombatService.RosterViewModel.RollAttackTarget(ActiveList());
        }


        private void Auto_Act_Button_Click(object sender, RoutedEventArgs e)
        {
            if (!CheckValidActive())
                return;
            CombatService.RosterViewModel.PerformAutomaticActions(ActiveList());
        }

        internal bool CheckValidActive()
        {
            if (CombatService.RosterViewModel.SelectedActiveCharacter == null &&
                CombatService.RosterViewModel.SelectedDeceasedCharacter == null)
            {
                MessageBox.Show("Please select an active character.");
                return false;
            }
            return true;
        }

        private void Results_TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            Results_RichTextBox.ScrollToEnd();
        }

        private void DataChanged_DataGrid(object sender, EventArgs e)
        {
            CombatService.RosterViewModel.RecalculateCombatStats(ActiveList());
        }

        private void Make_Status_Button_Click(object sender, RoutedEventArgs e)
        {
            if (CheckValidActive())
            {
                var se = new StatusEffectWindow();
                se.ShowDialog();
                if (!se.WasCancel && !String.IsNullOrWhiteSpace(se.StatusDescription.Text))
                {
                    int duration = Int32.Parse(se.StatusDuration.Text);
                    String description = se.StatusDescription.Text;
                    CombatService.RosterViewModel.AssignStatus(ActiveList(), duration, description);
                }
            }
        }


        private void CleanDeceasedCharacters_Button_Click(object sender, RoutedEventArgs e)
        {
            CombatService.RosterViewModel.MarkCharactersAsDeceased();
        }

        private void RemoveCharacter_Button_Click(object sender, RoutedEventArgs e)
        {
            if (!CheckValidActive())
                return;
            CombatService.RosterViewModel.RemoveCharactersFromRosters(ActiveList());
        }

        internal IList ActiveList()
        {
            if (ActiveCharacters_ListBox.SelectedItems.Count == 0)
            {
                return DeceasedCharacters.SelectedItems;
            }
            return ActiveCharacters_ListBox.SelectedItems;
        }

        private void AddVisual_Button_Click(object sender, RoutedEventArgs e)
        {
            if (ActiveCharacters_ListBox.SelectedItems.Count == 0)
            {
                MessageBox.Show("Please select at least one active character.");
                return;
            }
            String possibleName = (ActiveCharacters_ListBox.SelectedItems[0] as CharacterSheet).Name;
            var _visualWindow = new SelectVisualWindow(possibleName, _pictureSelectionViewModel,
                CombatService.RosterViewModel.Teams);
            _visualWindow.ShowDialog();
            var pictureInfo = _visualWindow.SearchedDisplayItems_ListBox.SelectedItem as PictureFileInfo;
            Color pieceColor = _visualWindow.ChosenColor;
            Team chosenTeam = _visualWindow.ChosenTeam;
            if (!_visualWindow.WasCancel && pictureInfo != null && pieceColor != null)
            {
                CombatService.RosterViewModel.AddVisualToCharacters(ActiveCharacters_ListBox.SelectedItems, pictureInfo,
                    chosenTeam);
            }
        }

        private void ChangeBackground_Button_Click(object sender, RoutedEventArgs e)
        {
            var o = new OpenFileDialog();
            o.InitialDirectory = Directory.GetCurrentDirectory() + @"\MapPictures\";
            o.Multiselect = false;
            o.ShowDialog();
            if (!String.IsNullOrWhiteSpace(o.FileName))
            {
                var sbd = new SetBoardDimensions(VisualsViewModel.Instance.CurrentBoardInfo.DefinedBoardHeight,
                    VisualsViewModel.Instance.CurrentBoardInfo.DefinedBoardWidth);
                sbd.ShowDialog();
                if (!sbd.WasCancel && sbd.HasBoardHeight && sbd.HasBoardWidth)
                {
                    string targetFile = o.FileName.Replace(
                        Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar, "");

                    VisualsViewModel.Instance.SetBoardBackground(
                        targetFile, 
                        sbd.BoardHeight, 
                        sbd.BoardWidth, 
                        HillValue_Slider.Value,
                        sbd.MaintainRatio);
                }
            }
        }


        private void TeamColor_ActiveCharacters_ListBox_Click(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                //ActiveCharacters_ListBox.SelectedItems.Clear();
                Team team = (ActiveCharacters_ListBox.SelectedItem as CharacterSheet).Team;
                if (team != null)
                {
                    ActiveCharacters_ListBox.SelectedItems.Clear();
                    var visuals = new List<Guid>();
                    foreach (var cur in team.TeamMembers)
                    {
                        CharacterSheet sheet = cur;
                        if (sheet.HasVisual)
                        {
                            visuals.Add(sheet.UniqueCharacterID);
                        }
                        ActiveCharacters_ListBox.SelectedItems.Add(cur);
                    }
                    if (visuals.Count > 0)
                    {
                        BoardsZoomTo(visuals);
                    }
                    e.Handled = true;
                }
            }
        }

        private void BoardsZoomTo(List<Guid> visuals)
        {
            var targetType = BoardsViewModel.BoardTypes.All;
            if (CombatService.RosterViewModel.OrientAllCamerasToMatchMain)
            {
                targetType = BoardsViewModel.BoardTypes.Main;
            }
            VisualsService.BoardsViewModel.ZoomTo(visuals, targetType);
        }

        private void Target_ActiveCharacters_ListBox_Click(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                foreach (var cur in ActiveCharacters_ListBox.SelectedItems)
                {
                    var selectedCharacter = cur as CharacterSheet;

                    if (selectedCharacter.HasVisual && selectedCharacter.Target != null
                        && selectedCharacter.Target.HasVisual)
                        VisualsViewModel.Instance.DrawAttack(selectedCharacter.UniqueCharacterID,
                            selectedCharacter.Target.UniqueCharacterID,
                            selectedCharacter.Team.TeamColor,
                            new Duration(new TimeSpan(0, 0, 0, 0, 800)));
                }
            }
        }


        private void ZoomToVisual_Click(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
            {
                foreach (var cur in ActiveCharacters_ListBox.SelectedItems)
                {
                    var selectedCharacter = cur as CharacterSheet;

                    if (selectedCharacter.HasVisual)
                    {
                        BoardsZoomTo(new List<Guid> { selectedCharacter.UniqueCharacterID });
                    }
                }
            }
        }

        private void RestoreDeceased_Button_Click(object sender, RoutedEventArgs e)
        {
            CombatService.RosterViewModel.MoveDeceasedToActive();
        }

        private void GridIsChecked_ToggleButton(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.DrawGrid(5);
        }

        private void GridIsUnchecked_ToggleButton(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.RemoveGrid();
        }

        private void OpenFile_Click_MenuItem(object sender, RoutedEventArgs e)
        {
            var readBoardsSuccessful = FileSaveOpenService.OpenFile();
            var trimmedFileName = FileSaveOpenService.PreviousFileName.Substring(0,
                Math.Min(FileSaveOpenService.PreviousFileName.Length, 25));
            ResetTitleToSaveTitle();
            MakeDefaultBoards();
        }

        private void ResetTitleToSaveTitle()
        {
            if (!String.IsNullOrWhiteSpace(FileSaveOpenService.PreviousFileName))
            {
                Title = "Combat Automation Theater - " + FileSaveOpenService.PreviousFileName;
            }
        }

        private void SaveFile_Click_MenuItem(object sender, RoutedEventArgs e)
        {
            FileSaveOpenService.SaveFile();
            ResetTitleToSaveTitle();
        }

        private void SaveAsFile_Click_MenuItem(object sender, RoutedEventArgs e)
        {
            FileSaveOpenService.SaveFileAs();
            ResetTitleToSaveTitle();
        }

        private void Exit_Click_MenuItem(object sender, RoutedEventArgs e)
        {
            Environment.Exit(0);
        }

        private void Auto_Run_Combat(object sender, RoutedEventArgs e)
        {
            if (
                MessageBox.Show("Run entire combat until only one team remains?", "Confirm Auto Combat Mode",
                    MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.No)
            {
            }
            else
            {
                var teams = CombatService.RosterViewModel.ActiveRoster.Select(x => x.Team).Distinct();
                if (teams.Count() <= 1)
                {
                    MessageBox.Show("Two teams must exist.");
                }
                while (teams.Count() != 1)
                {
                    Initiative_Button_Click(this, null);
                    ActiveCharacters_ListBox.SelectAll();
                    Auto_Act_Button_Click(this, null);
                }
            }
        }

        private void ActivateNext_Button_Click(object sender, RoutedEventArgs e)
        {
            ActivateNextValid();
        }

        private void ActivateNextValid()
        {
            var firstChar = CombatService.RosterViewModel.ActiveRoster.Where(x =>
                !x.HasMoved &&
                !x.HasAttacked &&
                !x.IsIncapacitated).OrderByDescending(x => x.CurInitiative).FirstOrDefault();

            if (firstChar != null && ActiveCharacters_ListBox.Items.Contains(firstChar))
            {
                var curChar = ActiveCharacters_ListBox.SelectedItem as CharacterSheet;
                if (curChar == firstChar)
                {
                    curChar.HasAttacked = true;
                    curChar.HasMoved = true;
                }
                ClearSelectedPieces(this, null);
                ActivateCharacter(firstChar, true);
            }
            else
            {
                TextReporter.Report("No valid characters to select.\n");
            }
        }

        private void RollTraits_Button_Click(object sender, RoutedEventArgs e)
        {
            var sheets = new List<CharacterSheet>();
            foreach (var cur in ActiveList())
            {
                sheets.Add(cur as CharacterSheet);
            }
            var RT = new RollTraitsWindow(sheets);
            RT.ShowDialog();
            if (!RT.WasCancel)
            {
                CombatService.RosterViewModel.RollCharacters(ActiveList(), RT.SelectedTraitLabels.ToList());
            }
        }

        private void DownloadCharacters_Click_MenuItem(object sender, RoutedEventArgs e)
        {
            var smw = new ServerManagementWindow();
            smw.CharacterDownloadedReporter.WebCharacterCreated += CombatService.RosterViewModel.ServerCharacterReceived;
            smw.ShowDialog();
        }

        private void GridTroopFormation_Button_Click(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.MoveSelectedPiecesToFormation(VisualsViewModel.TroopFormation.Grid);
        }

        private void CircleFormation_Button_Click(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.MoveSelectedPiecesToFormation(VisualsViewModel.TroopFormation.Circle);
        }

        private void RandomFormation_Button_Click(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.MoveSelectedPiecesToFormation(VisualsViewModel.TroopFormation.Random);
        }

        private void TightFormation_Button_Click(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.MoveSelectedPiecesToFormation(VisualsViewModel.TroopFormation.Tight);
        }

        private void ExpandMap_ToggleButtonChanged(object sender, RoutedEventArgs e)
        {
            bool isChecked = ExpandMap_ToggleButton != null
                             && (bool)ExpandMap_ToggleButton.IsChecked;
            if (isChecked)
            {
                VisualsViewModel.Instance.ShowMapExpansion(true);
            }
            else
            {
                VisualsViewModel.Instance.ShowMapExpansion(false);
            }
        }

        private void ToggleInvisible_Button_Click(object sender, RoutedEventArgs e)
        {
            var activeCharacters = new List<Guid>();
            foreach (var cur in ActiveList())
            {
                var curSheet = cur as CharacterSheet;

                activeCharacters.Add(curSheet.UniqueCharacterID);
                if (curSheet.IsInvisibleOnAtLeastOneBoard)
                {
                    curSheet.IsInvisibleOnAtLeastOneBoard = false;
                }
                else
                {
                    curSheet.IsInvisibleOnAtLeastOneBoard = true;
                }
            }
            VisualsService.BoardsViewModel.ToggleInvisible(activeCharacters, BoardsViewModel.BoardTypes.Window);
        }

        private void NewWindow_Button_Click(object sender, RoutedEventArgs e)
        {
            MakeNewGameBoardWindow();

        }

        private void MakeNewGameBoardWindow()
        {
            var boardForWindow =
            VisualsService.BoardsViewModel.CreateAndRegisterNewBoard(BoardsViewModel.BoardTypes.Window);
            var boardVisualWindow = new GameBoardVisual_Window(boardForWindow);
            _gameBoardWindows.Add(boardVisualWindow);


            boardVisualWindow.Show();
        }

        private void ChangeVertical_Button_Click(object sender, RoutedEventArgs e)
        {
            var activeCharacters = new List<Guid>();
            foreach (var cur in ActiveList())
            {
                var curSheet = cur as CharacterSheet;
                curSheet.IsFlier = !curSheet.IsFlier;
                if (curSheet.IsFlier)
                {
                    VisualsViewModel.Instance.ChangeElevationBy(curSheet.UniqueCharacterID, 30);
                }
                else
                {
                    VisualsViewModel.Instance.SetElevationTo(curSheet.UniqueCharacterID, 0);
                }
                

            }
        }

        private void IncreaseElevation_Button_Click(object sender, RoutedEventArgs e)
        {
            foreach (var cur in ActiveList())
            {
                var curSheet = cur as CharacterSheet;
                VisualsViewModel.Instance.ChangeElevationBy(curSheet.UniqueCharacterID, 5);
            }
        }

        private void DecreaseElevation_Button_Click(object sender, RoutedEventArgs e)
        {
            foreach (var cur in ActiveList())
            {
                var curSheet = cur as CharacterSheet;
                VisualsViewModel.Instance.ChangeElevationBy(curSheet.UniqueCharacterID, -5);
            }
        }

        private void GenerateHills_Button_Click(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.SetHillsToLevel(HillValue_Slider.Value);
        }

        private void ResetHillSeed_Button_Click(object sender, RoutedEventArgs e)
        {
            VisualsViewModel.Instance.ChangeBoardSeed();
        }

        
    }
}